<?php

// Namespace

namespace Loop;


/**
 * CUser is a class that takes care of sign-up, sign-in and sign-out of users
 *
 * @package		Loop
 * @category	Model
 */

class CUser extends \Loop\CObject
{


	/**
	 *
	 */
	
	public function api_completeResetPassword()
	{
		// Get API arguments

		$userId = fv('userId', true);
		$userPassword = fv('userPassword', true);
		$userReference = fv('userReference', false);
		$userResetKey = fv('userResetKey', true);
		$userValidationKey = fv('userValidationKey', true);


		// Load the user

		$this->loadSecure($userId, $userReference);

		
		// Call the method
		
		$this->completeResetPassword($userValidationKey, $userResetKey, $userPassword);


		// Define URL of redirection
		
		$urlOfRedirection = v('SESSION::urlOfRedirection');

		if (empty($urlOfRedirection) === true)
		{
			$urlOfRedirection = app()->getPathHelper()->convertPathToUrl(pref('[Loop]/views/pathToPageHome.html'));
		}

		v('SESSION::urlOfRedirection', '');


		// Prepare result
		
		$result = array
		(
			'urlOfRedirection' => $urlOfRedirection
		);


		return $result;
	}
	
	
	/**
	 *
	 */
	
	public function api_setLanguage()
	{
		// Get API arguments
		
		$languageIsoCode = fv('languageIsoCode', true);
		
		
		// Call the method
		
		$result = user()->setLanguage($languageIsoCode);

		
		return $result;
	}
	
		
	/**
	 *
	 */
	
	public function api_signIn()
	{
		// Get API arguments

		$userEmail = fv('userEmail', true);
		$userPassword = fv('userPassword', true);
		
		
		// Call the method
		
		$urlOfRedirection = $this->signIn($userEmail, $userPassword);


		// If urlOfRedirection is empty, redirect on home page

		if (empty($urlOfRedirection) === true)
		{
			$urlOfRedirection = url();
		}
		
		
		// Prepare result
		
		$result = array
		(
			'urlOfRedirection' => $urlOfRedirection
		);
		
		
		return $result;
	}
	
	
	/**
	 *
	 */
	
	public function api_signOut()
	{
		// Call the method
		
		$result = user()->signOut();
		
		
		return $result;
	}
	
	
	/**
	 *
	 */
	
	public function api_signUp()
	{
		// Get API arguments
		
		$userEmail = fv('userEmail', true);
		$userPassword = fv('userPassword', true);
		
		
		// Call the method
		
		$result = $this->signUp($userEmail, $userPassword);
		
		
		return $result;
	}
	

	/**
	 *
	 */
	
	public function api_startResetPassword()
	{
		// Get API arguments

		$userEmail = fv('userEmail', true);


		// Call the method

		$result = $this->startResetPassword($userEmail);


		return $result;
	}
	
	
	/**
	 *
	 */
	
	public function api_validate()
	{
		$user = user();
		
		$user->validate($arguments['userValidationKey']);
		$user->forceSignIn();
	}
	
	
	/**
	 *
	 */
	
	public function completeResetPassword($userValidationKey, $userResetKey, $userPassword)
	{
		// Does the current user match with the reset key?

		if
		(
			($this->getValidationKey() !== $userValidationKey) ||
			($this->getResetKey() !== $userResetKey)
		)
		{
			e(EXCEPTION_USER_RESET_KEY_NOT_VALID);
		}


		// Set the password and empty the reset key

		$this->setEncryptedPassword(app()->getStringHelper()->encrypt($userPassword));
		$this->setResetKey(null);

		$this->save();
		
		
		// Now sign user in!
		
		$this->signIn($this->getEmail(), $userPassword);
		
		
		//

		$extensionId = pref('[Loop]/model/CUser/extensionId');

		if (empty($extensionId) === true)
		{
			$extensionId = EXTENSION_LOOP;
		}


		// Notify user about the change
		
		notify
		(
			NOTIFICATION_EMAIL,
			$extensionId,
			'User.CompleteResetPassword',
			$this->getEmail(),
			$this->getDefaultName(),
			array
			(
				'userPassword' => $userPassword
			)
		);
	}
	
	
	/**
	 *
	 */
	
	protected function createProperties()
	{
		$this->createProperty
		(
			'email',
			array
			(
				'isName' => true,
				'type' => TYPE_EMAIL
			)
		);
		
		$this->createProperty
		(
			'encryptedPassword',
			array
			(
				'type' => TYPE_STRING_MEDIUM
			)
		);
		
		$this->createProperty
		(
			'ipAddress',
			array
			(
				'type' => TYPE_STRING_SHORT
			)
		);
		
		$this->createProperty
		(
			'languageIsoCode',
			array
			(
				'type' => TYPE_LANGUAGE_ISO_CODE
			)
		);
		
		$this->createProperty
		(
			'referralKey',
			array
			(
				'type' => TYPE_STRING_LONG
			)
		);
		
		$this->createProperty
		(
			'resetKey',
			array
			(
				'type' => TYPE_STRING_LONG
			)
		);
		
		$this->createProperty
		(
			'timeOfSignIn',
			array
			(
				'type' => TYPE_TIMESTAMP
			)
		);
		
		$this->createProperty
		(
			'timeOfSignUp',
			array
			(
				'type' => TYPE_TIMESTAMP
			)
		);
		
		$this->createProperty
		(
			'timeOfValidation',
			array
			(
				'type' => TYPE_TIMESTAMP
			)
		);
		
		$this->createProperty
		(
			'universe',
			array
			(
				'type' => TYPE_STRING_SHORT
			)
		);
		
		$this->createProperty
		(
			'userGroupId',
			array
			(
				'className' => 'CUserGroup',
				'extensionId' => EXTENSION_LOOP,
				'isAssociation' => true
			)
		);
		
		$this->createProperty
		(
			'validationKey',
			array
			(
				'type' => TYPE_STRING_LONG
			)
		);
	}
	
	
	/**
	 *
	 */
	
	public function forceSignIn()
	{
		// Store the user ID an reference
		
		if (pref('[Loop]/authentication/forceSignIn') == true)
		{
			// Force sign-in is ON, so store the email and encrypted password
			
			v
			(
				pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userEmail'),
				$this->getEmail()
			);
			
			v
			(
				pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userEncryptedPassword'),
				$this->getEncryptedPassword()
			);
		}
		else
		{
			// Force sign-in is OFF, so store the user ID+reference
			
			v
			(
				pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userId'),
				$this->getId()
			);
			
			v
			(
				pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userReference'),
				$this->getReference()
			);
		}


		// Update the universe

		pref('[Loop]/application/universe', $this->getUniverse());
	}
	
	
	/**
	 * Checks whether a user is signed in or not
	 *
	 * @return	bool True if signed in, false otherwise
	 */
	
	public function isSignedIn()
	{
		if (pref('[Loop]/authentication/forceSignIn') == true)
		{
			return
			(
				($this->isLoaded() == true) &&
				($this->getEmail() == v(pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userEmail'))) &&
				($this->getEncryptedPassword() == v(pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userEncryptedPassword')))
			);
		}
		else
		{
			return
			(
				($this->isLoaded() == true) &&
				($this->getId() == v(pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userId'))) &&
				($this->getReference() == v(pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userReference')))
			);
		}
	}
	
	
	/**
	 * Finds a user and resets its password
	 */
	
	public function startResetPassword($userEmail)
	{
		// Find the user
		
		$rawUsers = $this->find
		(
			array
			(
				'conditions' => array
				(
					'email' => array(EQUAL, $userEmail)
				)
			)
		);


		// Do we have a unique user?
		
		if (count($rawUsers) != 1)
		{
			e
			(
				EXCEPTION_USER_NOT_FOUND,
				array
				(
					'userEmail' => $userEmail
				)
			);
		}
		
		
		// Load the user
		
		$rawUser = array_pop($rawUsers);
		$this->loadFromRawObject($rawUser);


		// Set a reset key
		
		$resetKey = $this->getResetKey();
		
		if (empty($resetKey) === true)
		{
			$this->setResetKey(app()->getStringHelper()->generateUniqueKey());
			$this->save();
		}
		

		// Define extension id (are we use using Login ?)

		$extensionId = 'Loop';

		if ((pref('[Loop]/application/code')) != "")
		{
			$extensionId = 'Login';
		}


		//

		$extensionId = pref('[Loop]/model/CUser/extensionId');

		if (empty($extensionId) === true)
		{
			$extensionId = EXTENSION_LOOP;
		}


		// Send an e-mail to notify user
		
		notify
		(
			NOTIFICATION_EMAIL,
			$extensionId,
			'User.StartResetPassword',
			$this->getEmail(),
			$this->getDefaultName(),
			array
			(
				'url' => url
				(
					'[' . $extensionId . ']/page/reset-password/',
					array
					(
						'ue' => $this->getEmail(),
						'ui' => $this->getId(),
						'ur' => $this->getReference(),
						'urk' => $this->getResetKey(),
						'uvk' => $this->getValidationKey()
					)
				)
			)
		);
	}
	
	
	/**
	 * Sets the current language
	 *
	 * @param	string $languageIsoCode The ISO code of the language (e.g. en, fr, es, cn)
	 */
	
	public function setLanguage($languageIsoCode)
	{
		// Set the new language preference
		
		pref('[Loop]/localization/languageIsoCode', $languageIsoCode);


		// Save it

		$this->setLanguageIsoCode($languageIsoCode);

		$this->save();
	}
	
	
	/**
	 * Signs a user in from their credentials
	 *
	 * @param	string	$userEmail				The email of the user
	 * @param	string	$userPassword			The password of the user (unencrypted)
	 * @param	bool		$isPasswordEncrypted	True if password is encrypted, false otherwise
	 */
	
	public function signIn($userEmail, $userPassword, $isPasswordEncrypted = false)
	{
		// Is this feature enabled?
		
		if (pref('[Loop]/features/authentication') == false)
		{
			return;
		}
		
		
		// No need to go further if no user email
		
		$userEmail = trim($userEmail);
		
		if (empty($userEmail) == true)
		{
			e(EXCEPTION_USER_AUTHENTICATION_NEEDED);
		}
		
		
		// Try to get the user that matches the email
		
		$rawUsers = $this->find
		(
			array
			(
				'conditions' => array
				(
					'email' => array(EQUAL, $userEmail)
				)
			)
		);
		
		
		// Could we find it?
		
		$nbUsers = count($rawUsers);
		
		if ($nbUsers == 0)
		{
			e
			(
				EXCEPTION_USER_NOT_FOUND,
				array
				(
					'userEmail' => $userEmail
				)
			);
		}
		else if ($nbUsers > 1)
		{
			e
			(
				EXCEPTION_USER_TOO_MANY_FOUND,
				array
				(
					'nbUsers' => $nbUsers,
					'userEmail' => $userEmail
				)
			);
		}
		
		
		// Create a temporary user

		$user = new $this->className();

		$rawUser = array_pop($rawUsers);
		$user->loadFromRawObject($rawUser);

		
		// Check whether passwords match

		if ($isPasswordEncrypted == true)
		{
			$encryptedPassword1 = $userPassword;
		}
		else
		{
			$encryptedPassword1 = app()->getStringHelper()->encrypt($userPassword);
		}
		
		$encryptedPassword2 = $user->getEncryptedPassword();
		
		if ($encryptedPassword1 != $encryptedPassword2)
		{
			e
			(
				EXCEPTION_USER_PASSWORD_NOT_VALID,
				array('encryptedPassword1' => $encryptedPassword1, 'encryptedPassword2' => $encryptedPassword2)
			);
		}
		
		
		// Time to load the user for good
		
		$this->loadFromRawObject($rawUser);


		// Force sign-in
		
		$this->forceSignIn();
	}
	
	
	/**
	 * Signs a user in from the source (either a cookie or session)
	 */
	
	public function signInFromSource()
	{
		// Is this feature enabled?
		
		if (pref('[Loop]/features/authentication') == false)
		{
			return;
		}
		
		
		// Do we need to sign-in again with login+password?
		
		if (pref('[Loop]/authentication/forceSignIn') == true)
		{
			// Sign-in with credentials
			
			$this->signIn
			(
				v(pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userEmail')),
				v(pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userEncryptedPassword')),
				true
			);
		}
		else
		{
			// Try to securely load the user
			
			try
			{
				$this->loadSecure
				(
					v(pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userId')),
					v(pref('[Loop]/authentication/source') . '::' . pref('[Loop]/authentication/userReference'))
				);
			}
			catch (\Exception $e)
			{
				if ($e->getCode() != EXCEPTION_OBJECT_NOT_FOUND)
				{
					throw $e;
				}
			}
		}
		
		
		// Good, we're allowed to be signed-in
		
		$this->forceSignIn();


		// Remember that we've just signed-in

		if ($this->isLoaded() === true)
		{
			$this->setTimeOfSignIn(time());
			$this->setIpAddress(v('SERVER::REMOTE_ADDR'));
			$this->save();
		}
	}
	
	
	/**
	 * Signs a user out
	 */
	
	public function signOut()
	{
		// Is this feature enabled?
		
		if (pref('[Loop]/features/authentication') == false)
		{
			return;
		}
		
		
		// Reset the session
		
		app()->getSessionManager()->reset();
		
		
		// Reset the user
		
		$this->reset();
	}
	
	
	/**
	 * Signs a user up
	 *
	 * @param	string	$userEmail		The email of the user
	 * @param	string	$userPassword	The password of the user (unencrypted)
	 *
	 * @return	int The ID of the user
	 */
	
	public function signUp($userEmail, $userPassword)
	{
		$stringHelper = app()->getStringHelper();


		// Set the user properties
		
		$this->setEmail($userEmail);
		$this->setEncryptedPassword($stringHelper->encrypt($userPassword));
		$this->setValidationKey($stringHelper->generateUniqueKey());
		
		
		// Save the user
		
		$this->save();
		
		
		return $this->getId();
	}
}

?>
